package offer

import (
	// "bp/cache"
	sqlc "bp/db/sqlc"
	memberCoupon "bp/internal/service/entity/member_coupon"
	offTrial "bp/internal/service/entity/off_trial"
	"bp/internal/service/entity/promotion"
	"bp/internal/service/param"
	// "bp/internal/service/com/offer"
	"bp/internal/util"
	"context"
	"database/sql"
)

// DoTrialParam 试算优惠参数
type DoTrialParam struct {
	OutTransNo  string
	TrialScene  string
	TrialIssuer string
	OutCardID   int64
	OwnerPtrs   []*ToTrialOwner
}

// DoTrial 试算优惠
func (s *OfferComServiceImp) DoTrial(ctx context.Context, q *sqlc.Queries, ptr *DoTrialParam) (
	off int32,
	trialedOwnerPtrs []*TrialedOwner,
	trialedOfferItemPtrOfCardDiscount *TrialedOfferItem,
	err error) {
	slog := util.NewContextLogger(ctx, s.slog)
	token := util.GetAuthorizationToken(ctx)

	canUseCardDiscount := s.checkCanUseCardDiscount(ptr.TrialScene, ptr.OutCardID)
	// 按照每个owner独立来算
	for _, ownerPtr := range ptr.OwnerPtrs {
		var trialedOfferItemPtrs []*TrialedOfferItem
		itemPtrs := ownerPtr.ItemPtrs
		offOfOwner, offTrialPtrsOfOwner, offTrialItemPtrsOfOwner, doErr := s.doTrialOfOwner(ctx, q, token, ptr.OutTransNo, ptr.TrialScene, ptr.TrialIssuer, ownerPtr.OwnerID, ownerPtr.MustUseCouponSns, itemPtrs)
		if doErr != nil {
			err = doErr
			return
		}
		off += offOfOwner
		trialedOfferItemPtrs = append(trialedOfferItemPtrs, s.MapOffTrialPtrsToTrialedOfferItemPtrs(offTrialPtrsOfOwner, offTrialItemPtrsOfOwner)...)
		trialedOwnerPtrs = append(trialedOwnerPtrs, &TrialedOwner{
			OwnerID:            ownerPtr.OwnerID,
			Off:                offOfOwner,
			TrialedOffItemPtrs: trialedOfferItemPtrs,
		})
		slog.Infof("DoTrialOfOwner ok")
		// 试算场景需要把卡折扣的多条试算offeritem合并
		if canUseCardDiscount {
			remainAmount := s.getItemRawAmount(itemPtrs) - off
			slog.Infof("remainAmount=>%d", remainAmount)
			offOfCardDiscount, offTrialPtrOfCardDiscount, offTrialItemPtrsOfCardDiscount, doErr2 := s.doTrialCardDiscountAfterOwnerTrial(ctx, q, token, ptr.OutTransNo, ptr.TrialScene, ptr.TrialIssuer, ownerPtr.OwnerID, remainAmount, ptr.OutCardID, itemPtrs)
			if doErr2 != nil {
				err = doErr2
				return
			}
			if trialedOfferItemPtrOfCardDiscount == nil {
				trialedOfferItemPtrOfCardDiscount = s.ConvOffTrialPtrToTrialedOfferItemPtr(offTrialItemPtrsOfCardDiscount)(offTrialPtrOfCardDiscount)
			} else {
				trialedOfferItemPtrOfCardDiscount.Off += offOfCardDiscount
			}
			off += offOfCardDiscount
			slog.Infof("DoTrialCardDiscountAfterOwnerTrial ok")
		}
	}

	return
}

type TrialProcedureInfo struct {
	UpsertOffTrialParamPtr      *sqlc.UpsertOffTrialParams
	UpsertOffTrialItemParamPtrs []*sqlc.UpsertOffTrialItemParams
}

type PromotionIDToTrialProcedureInfoPtr map[int64]*TrialProcedureInfo
type MemberCouponSnToTrialProcedureInfoPtr map[string]*TrialProcedureInfo

// doTrialOfOwner 按owner试算优惠
func (s *OfferComServiceImp) doTrialOfOwner(ctx context.Context, q *sqlc.Queries,
	token *util.AuthorizationToken,
	outTransNo string,
	trialScene string,
	trialIssuer string,
	ownerID int64,
	mustUseCouponSns []string,
	itemPtrs []*ToTrialItem,
) (off int32, offTrialPtrs []*sqlc.OffTrial, offTrialItemPtrs []*sqlc.OffTrialItem, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	mapOfMemberCoupon := make(MemberCouponSnToTrialProcedureInfoPtr)
	mapOfPromotion := make(PromotionIDToTrialProcedureInfoPtr)
	unMatchedItemPtrs := itemPtrs

	// 1 根据TrialScene是结算并且用户选择了使用某些券
	if trialScene == string(sqlc.EmOfferUseSceneOrdering) && len(mustUseCouponSns) > 0 {
		memberCouponPtrs, err2 := s.entityHub.MemberCouponService.ListAvailableMemberCouponsBySnsForDoTrial(ctx, q, &memberCoupon.ListAvailableMemberCouponsBySnsForDoTrialParam{
			MemberID: token.MemberID,
			BrandID:  token.BrandID,
			OwnerID:  ownerID,
			Sns:      mustUseCouponSns,
		})
		if err2 != nil {
			err = err2
			return
		}
		slog.Infof("ListAvailableMemberCouponsBySnsForDoTrial (%d)条", len(memberCouponPtrs))

		couponIDs := s.entityHub.MemberCouponService.ConvToCouponIDs(memberCouponPtrs)
		itemMapOfMemberCoupon, getErr := s.entityHub.CouponService.GetMapOfCouponIDToItemIDsByCouponIDs(ctx, q, couponIDs)
		if getErr != nil {
			err = getErr
			return
		}
		couponMapOfMemberCoupon, getErr2 := s.entityHub.CouponService.GetMapOfCouponIDToCouponPtrByCouponIDs(ctx, q, couponIDs)
		if getErr2 != nil {
			err = getErr2
			return
		}

		// 找出满减券
		memberCouponPtrsOfOfferTypeQuotaCut := s.entityHub.MemberCouponService.FilterMemberCouponPtrs(memberCouponPtrs, func(ptr *sqlc.MemberCoupon) bool {
			itemIDs, ok := itemMapOfMemberCoupon[ptr.CouponID]
			return ptr.Nature == string(sqlc.EmCouponNatureVoucher) && ok && len(itemIDs) > 0
		})
		slog.Infof("符合条件的满减券 (%d)条", len(memberCouponPtrsOfOfferTypeQuotaCut))
		
		for _, ptr := range memberCouponPtrsOfOfferTypeQuotaCut {
			couponID := ptr.CouponID
			offOfMemberCouponOfQuotaCut, upsertOffTrialParamPtr, upsertOffTrialItemParamPtrs, unMatchedItemPtrsOfMemberCouponOfQuotaCut, applyErr := s.applyQuotaCutCouponNatureVoucherToTrialItems(&applyQuotaCutCouponNatureVoucherToTrialItemsParam{
				memberCouponPtr: ptr,
				couponPtr:       couponMapOfMemberCoupon[couponID],
				itemIDsOfCoupon: itemMapOfMemberCoupon[couponID],
				itemPtrs:        unMatchedItemPtrs,
				memberID:        token.MemberID,
				brandID:         token.BrandID,
				outTransNo:      outTransNo,
				trialScene:      trialScene,
				trialIssuer:     trialIssuer,
			})
			if applyErr != nil {
				err = applyErr
				return
			}
			if upsertOffTrialParamPtr != nil {
				off += offOfMemberCouponOfQuotaCut
				mapOfMemberCoupon[ptr.Sn] = &TrialProcedureInfo{
					UpsertOffTrialParamPtr:      upsertOffTrialParamPtr,
					UpsertOffTrialItemParamPtrs: upsertOffTrialItemParamPtrs,
				}
			}
			unMatchedItemPtrs = unMatchedItemPtrsOfMemberCouponOfQuotaCut
		}
		slog.Infof("满减券应用 (%d)条", len(mapOfMemberCoupon))
	}

	// 2 读取当前时间点owner下可用的促销活动
	promotionPtrs, err := s.entityHub.PromotionService.ListAvailablePromotionsForDoTrial(ctx, q, &promotion.ListAvailablePromotionsForDoTrialParam{
		BrandID:  token.BrandID,
		OwnerID:  ownerID,
		Issuer:   trialIssuer,
		UseScene: trialScene,
	})
	if err != nil {
		return
	}
	slog.Infof("ListAvailablePromotionsByForDoTrial (%d)条", len(promotionPtrs))

	itemMapOfPromotion, err := s.entityHub.PromotionService.GetMapOfPromotionIDToItemIDsByPromotionPtrs(ctx, q, promotionPtrs)
	if err != nil {
		return
	}
	// 找出单品促销活动的因为其优先级高
	promotionPtrsOfNatureItemOff := s.entityHub.PromotionService.FilterPromotionPtrs(promotionPtrs, func(ptr *sqlc.Promotion) bool {
		return ptr.Nature == string(sqlc.EmPromotionNatureItemOff)
	})
	slog.Infof("单品促销活动 (%d)条", len(promotionPtrsOfNatureItemOff))
	for _, ptr := range promotionPtrsOfNatureItemOff {
		promotionID := ptr.ID
		offOfPromotionNatureItemOff, upsertOffTrialParamPtr, upsertOffTrialItemParamPtrs, unMatchedItemPtrsPromotionNatureItemOff, applyErr := s.applyNatureItemOffPromotionToTrialItems(&applyPromotionToTrialItemsParam{
			promotionNatureItemOffPtr: ptr,
			itemIDsOfPromotion:        itemMapOfPromotion[promotionID],
			itemPtrs:                  unMatchedItemPtrs,
			memberID:                  token.MemberID,
			brandID:                   token.BrandID,
			outTransNo:                outTransNo,
			trialScene:                trialScene,
			trialIssuer:               trialIssuer,
		})
		if applyErr != nil {
			err = applyErr
			return
		}
		if upsertOffTrialParamPtr != nil {
			off += offOfPromotionNatureItemOff
			mapOfPromotion[promotionID] = &TrialProcedureInfo{
				UpsertOffTrialParamPtr:      upsertOffTrialParamPtr,
				UpsertOffTrialItemParamPtrs: upsertOffTrialItemParamPtrs,
			}
		}
		unMatchedItemPtrs = unMatchedItemPtrsPromotionNatureItemOff
	}
	slog.Infof("单品促销活动应用 (%d)条", len(mapOfPromotion))

	// 找出多品促销活动
	promotionPtrsOfNatureItemsOff := s.entityHub.PromotionService.FilterPromotionPtrs(promotionPtrs, func(ptr *sqlc.Promotion) bool {
		// itemIDs, ok := itemMapOfPromotion[ptr.ID]
		return ptr.Nature == string(sqlc.EmPromotionNatureItemsOff)
	})
	slog.Infof("多品促销活动 (%d)条", len(promotionPtrsOfNatureItemsOff))
	for _, ptr := range promotionPtrsOfNatureItemsOff {
		promotionID := ptr.ID
		offOfPromotionNatureItemsOff, upsertOffTrialParamPtr, upsertOffTrialItemParamPtrs, unMatchedItemPtrsPromotionNatureItemsOff, applyErr := s.applyNatureItemsOffPromotionToTrialItems(&applyPromotionToTrialItemsParam{
			promotionNatureItemOffPtr: ptr,
			itemIDsOfPromotion:        itemMapOfPromotion[promotionID],
			itemPtrs:                  unMatchedItemPtrs,
			memberID:                  token.MemberID,
			brandID:                   token.BrandID,
			outTransNo:                outTransNo,
			trialScene:                trialScene,
			trialIssuer:               trialIssuer,
		})
		if applyErr != nil {
			err = applyErr
			return
		}
		if upsertOffTrialParamPtr != nil {
			off += offOfPromotionNatureItemsOff
			mapOfPromotion[promotionID] = &TrialProcedureInfo{
				UpsertOffTrialParamPtr:      upsertOffTrialParamPtr,
				UpsertOffTrialItemParamPtrs: upsertOffTrialItemParamPtrs,
			}
		}
		unMatchedItemPtrs = unMatchedItemPtrsPromotionNatureItemsOff
	}
	slog.Infof("多品促销活动应用 (%d)条", len(mapOfPromotion))

	slog.Infof("=DoTrialOfOwner开始数据库操作")
	err = s.entityHub.OffTrialService.DisableOffTrialsByOutTransNoAndTrialScene(ctx, q, &offTrial.DisableOffTrialsByOutTransNoAndTrialSceneParam{
		OutTransNo: outTransNo,
		TrialScene: trialScene,
	})
	if err != nil {
		return
	}
	slog.Infof("DisableOffTrialsByOutTransNoAndTrialScene ok")
	for sn := range mapOfMemberCoupon {
		trialProcedureInfo := mapOfMemberCoupon[sn]
		offTrialPtrOfMemberCoupon, upErr := s.entityHub.OffTrialService.UpsertOffTrial(ctx, q, trialProcedureInfo.UpsertOffTrialParamPtr)
		if upErr != nil {
			err = upErr
			return
		}
		offTrialID := offTrialPtrOfMemberCoupon.ID
		for _, upsertOffTrialItemParamPtr := range trialProcedureInfo.UpsertOffTrialItemParamPtrs {
			upsertOffTrialItemParamPtr.OffTrialID = offTrialID
		}
		offTrialItemPtrsOfMemberCoupon, upErr2 := s.entityHub.OffTrialService.RefreshOffTrialItemsByOutTransNoAndTrialScene(ctx, q, &offTrial.RefreshOffTrialItemsByOutTransNoAndTrialSceneParam{
			OutTransNo:                   outTransNo,
			TrialScene:                   trialScene,
			UpsertOffTrialITemParamsPtrs: trialProcedureInfo.UpsertOffTrialItemParamPtrs,
		})
		if upErr2 != nil {
			err = upErr2
			return
		}

		offTrialPtrs = append(offTrialPtrs, offTrialPtrOfMemberCoupon)
		offTrialItemPtrs = append(offTrialItemPtrs, offTrialItemPtrsOfMemberCoupon...)
	}
	slog.Infof("off_trials of memberCoupons ok")
	for promotionID := range mapOfPromotion {
		trialProcedureInfo := mapOfPromotion[promotionID]
		slog.Infof("trialProcedureInfo :%+v", trialProcedureInfo)
		offTrialPtrOfPromotion, upErr := s.entityHub.OffTrialService.UpsertOffTrial(ctx, q, trialProcedureInfo.UpsertOffTrialParamPtr)
		if upErr != nil {
			err = upErr
			return
		}
		offTrialID := offTrialPtrOfPromotion.ID
		for _, upsertOffTrialItemParamPtr := range trialProcedureInfo.UpsertOffTrialItemParamPtrs {
			upsertOffTrialItemParamPtr.OffTrialID = offTrialID
		}
		offTrialItemPtrsOfPromotion, upErr2 := s.entityHub.OffTrialService.RefreshOffTrialItemsByOutTransNoAndTrialScene(ctx, q, &offTrial.RefreshOffTrialItemsByOutTransNoAndTrialSceneParam{
			OutTransNo:                   outTransNo,
			TrialScene:                   trialScene,
			UpsertOffTrialITemParamsPtrs: trialProcedureInfo.UpsertOffTrialItemParamPtrs,
		})
		if upErr2 != nil {
			err = upErr2
			return
		}

		offTrialPtrs = append(offTrialPtrs, offTrialPtrOfPromotion)
		offTrialItemPtrs = append(offTrialItemPtrs, offTrialItemPtrsOfPromotion...)
	}
	slog.Infof("off_trials of promotions ok")
	slog.Infof("=DoTrialOfOwner结束数据库操作")

	return
}

func (s *OfferComServiceImp) checkCanUseCardDiscount(trialScene string, outCardID int64) bool {
	return trialScene == string(sqlc.EmOfferUseSceneOrdering) && outCardID > 0
}

// DoTrialOfOwner 按owner试算交易后再交易试算优惠卡折扣
func (s *OfferComServiceImp) doTrialCardDiscountAfterOwnerTrial(ctx context.Context, q *sqlc.Queries,
	token *util.AuthorizationToken,
	outTransNo string,
	trialScene string,
	trialIssuer string,
	ownerID int64,
	remainAmount int32,
	outCardID int64,
	itemPtrs []*ToTrialItem,
) (off int32, offTrialPtrOfCardDiscount *sqlc.OffTrial, offTrialItemPtrs []*sqlc.OffTrialItem, err error) {
	slog := util.NewContextLogger(ctx, s.slog)
	var cardDiscountTrialProcedureInfoPtr *TrialProcedureInfo
	slog.Infof("根据TrialScene是结算并且用户选择了使用卡折扣")
	var needUseCardDiscount bool
	cardDiscountPtr, getErr := s.entityHub.CardDiscountService.GetCardDiscountPtrByOutCardID(ctx, q, outCardID)
	if getErr != nil {
		if getErr != sql.ErrNoRows {
			err = getErr
			return
		}
	} else {
		// < 100才存在折扣
		needUseCardDiscount = cardDiscountPtr.Discount < 100
	}
	
	if needUseCardDiscount {
		var isDiscountToAllOrder = true
		var offOfCardDiscount int32
		var upsertOffTrialParamPtr *sqlc.UpsertOffTrialParams
		if !isDiscountToAllOrder {
			var itemsOfCardDiscount []param.ItemID
			cardDiscountID := cardDiscountPtr.ID
			itemPtrsToTestUseCardDiscount := itemPtrs
			// 针对单个品进行匹配
			itemsOfCardDiscount, err = s.entityHub.CardDiscountService.ListItemIDsByCardDiscountID(ctx, q, cardDiscountID)
			if err != nil {
				return
			}
			offOfCardDiscount, upsertOffTrialParamPtr, upsertOffTrialItemParamPtrs, _, applyErr := s.applyCardDiscountToTrialItems(&applyCardDiscountToTrialItemsParam{
				cardDiscountPtr:       cardDiscountPtr,
				itemIDsOfCardDiscount: itemsOfCardDiscount,
				itemPtrs:              itemPtrsToTestUseCardDiscount,
				memberID:              token.MemberID,
				brandID:               token.BrandID,
				outTransNo:            outTransNo,
				trialScene:            trialScene,
				trialIssuer:           trialIssuer,
			})
			if applyErr != nil {
				err = applyErr
				return
			}
			off += offOfCardDiscount
			cardDiscountTrialProcedureInfoPtr = &TrialProcedureInfo{
				UpsertOffTrialParamPtr:      upsertOffTrialParamPtr,
				UpsertOffTrialItemParamPtrs: upsertOffTrialItemParamPtrs,
			}
		} else {
			slog.Infof("=======应用折扣:%+v", cardDiscountPtr)
			offOfCardDiscount, upsertOffTrialParamPtr, err = s.applyCardDiscountToRemainAmount(&applyCardDiscountToRemainAmountParam{
				cardDiscountPtr: cardDiscountPtr,
				amount:          remainAmount,
				memberID:        token.MemberID,
				brandID:         token.BrandID,
				outTransNo:      outTransNo,
				trialScene:      trialScene,
				trialIssuer:     trialIssuer,
			})
			if err != nil {
				return
			}
			off += offOfCardDiscount
			cardDiscountTrialProcedureInfoPtr = &TrialProcedureInfo{
				UpsertOffTrialParamPtr: upsertOffTrialParamPtr,
			}
		}
	}

	if cardDiscountTrialProcedureInfoPtr != nil {
		slog.Infof("=DoTrialCardDiscountAfterOwnerTrial开始数据库操作")
		trialProcedureInfo := cardDiscountTrialProcedureInfoPtr
		offTrialPtrOfCardDiscount, err = s.entityHub.OffTrialService.UpsertOffTrial(ctx, q, trialProcedureInfo.UpsertOffTrialParamPtr)
		if err != nil {
			return
		}
		if len(cardDiscountTrialProcedureInfoPtr.UpsertOffTrialItemParamPtrs) > 0 {
			offTrialID := offTrialPtrOfCardDiscount.ID
			for _, upsertOffTrialItemParamPtr := range trialProcedureInfo.UpsertOffTrialItemParamPtrs {
				upsertOffTrialItemParamPtr.OffTrialID = offTrialID
			}
			offTrialItemPtrsOfCardDiscount, upErr2 := s.entityHub.OffTrialService.RefreshOffTrialItemsByOutTransNoAndTrialScene(ctx, q, &offTrial.RefreshOffTrialItemsByOutTransNoAndTrialSceneParam{
				OutTransNo:                   outTransNo,
				TrialScene:                   trialScene,
				UpsertOffTrialITemParamsPtrs: trialProcedureInfo.UpsertOffTrialItemParamPtrs,
			})
			if upErr2 != nil {
				err = upErr2
				return
			}
			offTrialItemPtrs = append(offTrialItemPtrs, offTrialItemPtrsOfCardDiscount...)
		}
		slog.Infof("off_trials of card_discount ok")
		slog.Infof("=DoTrialCardDiscountAfterOwnerTrial结束数据库操作")
	}

	slog.Infof("卡折扣应用优惠试算 (%d)条", util.IntIfThenElse(cardDiscountTrialProcedureInfoPtr != nil, 1, 0))
	return
}
